#include "src/math/vector3d.h"

#include <cstdlib>
#include <cmath>
#include <iostream>

#include "src/math/vector.h"

Vector3D::Vector3D(){
	x = 0.0;
	y = 0.0;
	z = 0.0;
}

Vector3D::Vector3D(const Vector &v){
//    if(v.getRow() != 3){
//        std::cout <<"Erro no contrutor de Vector3D!"<<std::endl;
//        exit(1);
//    }
	x = v[0];
	y = v[1];
	z = v[2];
}

Vector3D::Vector3D(double _x, double _y, double _z):x(_x), y(_y), z(_z){
}

Vector3D::~Vector3D(){
}


double &Vector3D::operator[]( unsigned int _i){
#if 0
    return *((&x) + _i);
#else
    switch(_i){
        case 0:
            return x;
            break;
        case 1:
            return y;
            break;
        case 2:
            return z;
            break;
    }
#endif
    std::cout <<"Erro no operador [] de Vector3D!"<<std::endl;
    exit(1);
}

const double &Vector3D::operator[]( unsigned int _i) const {
#if 0
    return *((&x) + _i);
#else
    switch(_i){
        case 0:
            return x;
            break;
        case 1:
            return y;
            break;
        case 2:
            return z;
            break;
    }
    std::cout <<"Erro no operador [] de Vector3D const!"<<std::endl;
    exit(1);
#endif
}

bool Vector3D::operator==(const Vector3D &_vector)
{
	if( (x == _vector.x) && (y == _vector.y) && (z == _vector.z) ) return true;
	return false;
}

bool Vector3D::operator!=(const Vector3D &_vector)
{
	if( (x != _vector.x) || (y != _vector.y) || (z != _vector.z) ) return true;
	return false;
}

Vector3D& Vector3D::operator=(const Vector3D &_vector){
	if(this != &_vector){
		x = _vector.x;
		y = _vector.y;
		z = _vector.z;
	}
	return *this;
}

Vector3D Vector3D::operator*(double _s) const {
	return Vector3D(x*_s, y*_s, z*_s);
}

Vector3D Vector3D::operator/(double _s) const {
	return Vector3D(x/_s, y/_s, z/_s);
}

Vector3D Vector3D::operator*(const Vector3D &_vector) const {
    return Vector3D(x*_vector.x, y*_vector.y, z*_vector.z);
}

Vector3D Vector3D::operator/(const Vector3D &_vector) const {
    return Vector3D(x/_vector.x, y/_vector.y, z/_vector.z);
}

Vector3D Vector3D::operator+(const Vector3D &_vector) const {
	return Vector3D(x+_vector.x, y+_vector.y, z+_vector.z);
}

Vector3D Vector3D::operator+(const double &_s) const {
	return Vector3D(x+_s, y+_s, z+_s);
}

Vector3D Vector3D::operator-(const Vector3D &_vector) const {
	return Vector3D(x-_vector.x, y-_vector.y, z-_vector.z);
}

Vector3D Vector3D::operator-(const double &_s) const {
	return Vector3D(x-_s, y-_s, z-_s);
}

Vector3D Vector3D::operator-() const {
	return Vector3D(-x, -y, -z);
}

Vector3D Vector3D::crossProduct(const Vector3D &_vector) const {
	return Vector3D(y*_vector.z - z*_vector.y, z*_vector.x - x*_vector.z, x*_vector.y - y*_vector.x);
}

double Vector3D::dotProduct(const Vector3D &_vector) const {
	return x*_vector.x + y*_vector.y + z*_vector.z;
}

double Vector3D::length() const {
	return sqrt(this->dotProduct((*this)));
}

double Vector3D::length_2() const {
	return this->dotProduct((*this));
}

Vector3D Vector3D::normalize() const{
	double norm = length();
	return Vector3D(x/norm, y/norm, z/norm);
}

bool Vector3D::isNormalized() const{
	double norm = length();
	return ((norm - ERROR < 1.0) && (norm + ERROR > 1.0));
}

//double *Vector3D::intoVector() const {
//    double *d;
//    d = new double[3];
//    d[0] = x;
//    d[1] = y;
//    d[2] = z;
//
//    return d;
//}

void Vector3D::print() const {
    std::cout << "Vetor [ " <<x << ", " << y << ", " << z <<"]"<<std::endl;
}

Vector Vector3D::toHomogeneousCoordinates() const {
    Vector result(4);
    result[0] = x;
    result[1] = y;
    result[2] = z;
    result[3] = 1.0;
    return result;
}

GLfloat *Vector3D::toGL(GLfloat _v[]) const
{
    _v[0] = x;
    _v[1] = y;
    _v[2] = z;
    return _v;
}

double Vector3D::min() const
{
    double min = x;
    if(y < min) min = y;
    if(z < min) min = z;

    return min;
}

double Vector3D::max() const
{
    double max = x;
    if(y > max) max = y;
    if(z > max) max = z;

    return max;
}

Vector3D min(const Vector3D &_v1, const Vector3D &_v2)
{
    Vector3D result;
    if(_v1.x < _v2.x) result.x = _v1.x;
    else result.x = _v2.x;
    if(_v1.y < _v2.y) result.y = _v1.y;
    else result.y = _v2.y;
    if(_v1.z < _v2.z) result.z = _v1.z;
    else result.z = _v2.z;

    return result;
}

Vector3D max(const Vector3D &_v1, const Vector3D &_v2)
{
    Vector3D result;
    if(_v1.x > _v2.x) result.x = _v1.x;
    else result.x = _v2.x;
    if(_v1.y > _v2.y) result.y = _v1.y;
    else result.y = _v2.y;
    if(_v1.z > _v2.z) result.z = _v1.z;
    else result.z = _v2.z;

    return result;
}
